#ifndef FUNCTION_H
#define FUNCTION_H

class Function_One_Dimension
{
public:
    virtual double operator()(double x) const = 0;
    virtual double derivative(double x) const
    {
        const double h = 1e-5;
        return ((*this)(x + h) - (*this)(x - h)) / (2 * h);
    }
    virtual ~Function_One_Dimension() = default;
};

class Function_Two_Dimension
{
public:
    virtual double operator()(double x, double y) const = 0;
    virtual double derivative_x(double x, double y) const
    {
        const double h = 1e-5;
        return ((*this)(x + h, y) - (*this)(x - h, y)) / (2 * h);
    }
    virtual double derivative_y(double x, double y) const
    {
        const double h = 1e-5;
        return ((*this)(x, y + h) - (*this)(x, y - h)) / (2 * h);
    }
    virtual ~Function_Two_Dimension() = default;
};

class Function
{
public:
    virtual double operator()(double x, double y) const = 0;
    virtual double derivative_x(double x, double y) const
    {
        const double h = 1e-5;
        return ((*this)(x + h, y) - (*this)(x - h, y)) / (2 * h);
    }
    virtual double derivative_y(double x, double y) const
    {
        const double h = 1e-5;
        return ((*this)(x, y + h) - (*this)(x, y - h)) / (2 * h);
    }
    virtual ~Function() = default;
};

class InitialGuess0 : public Function_One_Dimension
{
public:
    double operator()(double x) const override
    {
        return 0;
    }
};

class InitialGuess1 : public Function_One_Dimension
{
public:
    double operator()(double x) const override
    {
        return 1;
    }
};

class function1 : public Function_One_Dimension
{
public:
    double operator()(double x) const override
    {
        return std::sin(M_PI * x); // sin(πx)
    }
};

class SourceTerm1 : public Function_One_Dimension
{
public:
    double operator()(double x) const override
    {
        return M_PI * M_PI * std::sin(M_PI * x); // π² sin(πx)
    }
};


class function2 : public Function_One_Dimension
{
public:
    double operator()(double x) const override
    {
        return std::exp(x * x); // e^(x^2)
    }
};

class SourceTerm2 : public Function_One_Dimension
{
public:
    double operator()(double x) const override
    {
        return -(4 * x * x + 2) * std::exp(x * x); // -(4x^2+2)*e^2
    }
};

class function3 : public Function_One_Dimension
{
public:
    double operator()(double x) const override
    {
        return std::exp(x + std::sin(x));
    }
};

class SourceTerm3 : public Function_One_Dimension
{
public:
    double operator()(double x) const override
    {
        return -(-std::sin(x) + std::cos(x) * std::cos(x) + 1) * std::exp(x + std::sin(x));
    }
};

class function4 : public Function_One_Dimension
{
public:
    double operator()(double x) const override
    {
        return sin(M_PI * x) * sin(M_PI * x);
    }
};

class SourceTerm4 : public Function_One_Dimension
{
public:
    double operator()(double x) const override
    {
        return 2 * M_PI * M_PI * sin(M_PI * x) * sin(M_PI * x);
    }
};

class function5 : public Function_One_Dimension
{
public:
    double operator()(double x) const override
    {
        return std::exp(std::sin(x) + std::cos(x));
    }
};

class SourceTerm5 : public Function_One_Dimension
{
public:
    double operator()(double x) const override
    {
        return -(-std::cos(x) - std::sin(x) + std::sin(x) * std::sin(x) + std::cos(x) * std::cos(x)) * std::exp(std::sin(x) + std::cos(x));
    }
};

class InitialGuess0_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return 0;
    }
};

class InitialGuess1_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return 1;
    }
};

// 目标解 u(x, y) = exp(y + sin(x))
class function1_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return std::exp(y + std::sin(x));
    }
};

class u1_1_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return std::exp(0 + std::sin(x));
    }
};

class u2_1_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return std::exp(y + std::sin(1));
    }
};

class u3_1_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return std::exp(1 + std::sin(x));
    }
};

class u4_1_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return std::exp(y + std::sin(0));
    }
};

class u1_partial_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return std::exp(0 + std::sin(x));
    }
};

class u2_partial_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        double u = std::exp(y + std::sin(1));
        return std::cos(1) * u;
    }
};

class u3_partial_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return std::exp(1 + std::sin(x));
    }
};

class u4_partial_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return std::cos(0) * exp(y + std::sin(0));
    }
};

// 源项 f(x, y) = -Δu = -(-sin(x) + cos^2(x) + 1) * exp(y + sin(x))
class SourceTerm_1_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        double u = std::exp(y + std::sin(x));
        double sin_x = std::sin(x);
        double cos_x = std::cos(x);
        double laplacian_u = (-sin_x + cos_x * cos_x + 1.0) * u;
        return -laplacian_u;
    }
};

// 目标解 u(x, y) = sin(πx) * sin(πy)
class function2_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return std::sin(M_PI * x) * std::sin(M_PI * y);
    }
};

class u1_2_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return 0;
    }
};

class u2_2_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return 0;
    }
};

class u3_2_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return 0;
    }
};

class u4_2_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return 0;
    }
};

// 源项 f(x, y) = -Δu = 2 * PI * PI * sin(PI * x) * sin(PI * y)
class SourceTerm_2_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        double laplacian_u = -2 * M_PI * M_PI * std::sin(M_PI * x) * std::sin(M_PI * y);
        return -laplacian_u;
    }
};

class u1_2_partial_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return M_PI * std::cos(M_PI * 0) * std::sin(M_PI * x);
    }
};

class u2_2_partial_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return M_PI * std::sin(M_PI * y) * std::cos(M_PI * 1);
    }
};

class u3_2_partial_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return M_PI * std::cos(M_PI * 1) * std::sin(M_PI * x);
    }
};

class u4_2_partial_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return M_PI * std::cos(M_PI * 0) * std::sin(M_PI * y);
    }
};

// 目标解 exp(sin(x) + cos(y))
class function3_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return std::exp(std::sin(x) + std::cos(y));
    }
};

// 源项 f(x, y) = -Δu = -(-cos(y) - sin(x) + sin^2(y) + cos^2(x)) * exp(sin(x) + cos(y))
class SourceTerm_3_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        double laplacian_u = std::exp(std::sin(x) + std::cos(y)) * (-std::cos(y) - std::sin(x) + std::sin(y) * std::sin(y) + std::cos(x) * std::cos(x));
        return -laplacian_u;
    }
};

class u1_3_partial_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return std::exp(std::sin(x) + std::cos(0)) * (-std::sin(0));
    }
};

class u3_3_partial_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return std::exp(std::sin(x) + std::cos(1)) * (-std::sin(1));
    }
};

class u2_3_partial_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return std::exp(std::sin(1) + std::cos(y)) * (std::cos(1));
    }
};

class u4_3_partial_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return std::exp(std::sin(0) + std::cos(y)) * (std::cos(0));
    }
};

class u1_3_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return std::exp(std::sin(x) + std::cos(0));
    }
};

class u2_3_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return std::exp(std::sin(1) + std::cos(y));
    }
};

class u3_3_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return std::exp(std::sin(x) + std::cos(1));
    }
};

class u4_3_2d : public Function_Two_Dimension
{
public:
    double operator()(double x, double y) const override
    {
        return std::exp(std::sin(0) + std::cos(y));
    }
};

#endif